<?php
namespace store\minio;

use store\minio\MinioAuthorization;
use store\minio\MinioConfig;
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
use Aws\S3\MultipartUploader;
use utils\MimeMapping;
use Aws\S3\Exception\S3Exception;
/*
 文件续传对象。负责将文件块写到服务器指定目录中
 
 更新记录：
 2019-05-22
  优化。
 2015-03-16
  修改创建文件的逻辑。将按实际文件大小创建一个大文件改为只创建一个字节的小文件，减少用户等待时间。
 2014-04-09
  修复Resumer方法中调用unlink($tmpPath);警告。
  新增CreateFile方法。
  增加文件块验证功能。
 2014-03-21 
  取消创建临时文件的操作，减少一次系统IO，直接读取临时文件。
 2012-03-30 
  创建
 */
class MinioTool
{
    function __construct()
    {
    }

    public static function client(){
        $cfg = new MinioConfig();
        $c = new \Aws\S3\S3Client([
                'version' => 'latest',
                'region'  => $cfg->region,
                'endpoint' => $cfg->endpoint,
                'use_path_style_endpoint' => true,
                'credentials' => [
                        'key'    => $cfg->ak,
                        'secret' => $cfg->sk,
                ]
        ]);
        return $c;
    }
    
    /*
     参考：https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#putobject
     语法：
        $result = $client->putObject([
        'ACL' => 'private|public-read|public-read-write|authenticated-read|aws-exec-read|bucket-owner-read|bucket-owner-full-control',
        'Body' => <string || resource || Psr\Http\Message\StreamInterface>,
        'Bucket' => '<string>', // REQUIRED
        'BucketKeyEnabled' => true || false,
        'CacheControl' => '<string>',
        'ChecksumAlgorithm' => 'CRC32|CRC32C|SHA1|SHA256',
        'ChecksumCRC32' => '<string>',
        'ChecksumCRC32C' => '<string>',
        'ChecksumSHA1' => '<string>',
        'ChecksumSHA256' => '<string>',
        'ContentDisposition' => '<string>',
        'ContentEncoding' => '<string>',
        'ContentLanguage' => '<string>',
        'ContentLength' => <integer>,
        'ContentSHA256' => '<string>',
        'ContentType' => '<string>',
        'ExpectedBucketOwner' => '<string>',
        'Expires' => <integer || string || DateTime>,
        'GrantFullControl' => '<string>',
        'GrantRead' => '<string>',
        'GrantReadACP' => '<string>',
        'GrantWriteACP' => '<string>',
        'Key' => '<string>', // REQUIRED
        'Metadata' => ['<string>', ...],
        'ObjectLockLegalHoldStatus' => 'ON|OFF',
        'ObjectLockMode' => 'GOVERNANCE|COMPLIANCE',
        'ObjectLockRetainUntilDate' => <integer || string || DateTime>,
        'RequestPayer' => 'requester',
        'SSECustomerAlgorithm' => '<string>',
        'SSECustomerKey' => '<string>',
        'SSECustomerKeyMD5' => '<string>',
        'SSEKMSEncryptionContext' => '<string>',
        'SSEKMSKeyId' => '<string>',
        'ServerSideEncryption' => 'AES256|aws:kms',
        'SourceFile' => '<string>',
        'StorageClass' => 'STANDARD|REDUCED_REDUNDANCY|STANDARD_IA|ONEZONE_IA|INTELLIGENT_TIERING|GLACIER|DEEP_ARCHIVE|OUTPOSTS|GLACIER_IR',
        'Tagging' => '<string>',
        'WebsiteRedirectLocation' => '<string>',
        ]);
     */
    public static function putObject(/*string*/$key,$data)
    {
        $cfg = new MinioConfig();
        $c = MinioTool::client();
        $ret = $c->putObject([
            "Bucket"=>$cfg->bucket,
            "Key"=>$key,
            "ContentType"=>MimeMapping::getType($key)]);
        return $ret;
    }

    /**
     * 参考：https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#getobject
     */
    public static function getObject($key,$blockOffset,$blockLen)
    {
        try {
            $cfg = new MinioConfig();
            $c = MinioTool::client();
            $ret = $c->getObject([
                "Bucket"=>$cfg->bucket,
                "Key"=>$key,
                "Range"=>"bytes $blockOffset-" . ($blockOffset + $blockLen)]);
            /*
             'AcceptRanges' => '<string>',
             'Body' => <string || resource || Psr\Http\Message\StreamInterface>,
             'BucketKeyEnabled' => true || false,
             'CacheControl' => '<string>',
             'ChecksumCRC32' => '<string>',
             'ChecksumCRC32C' => '<string>',
             'ChecksumSHA1' => '<string>',
             'ChecksumSHA256' => '<string>',
             'ContentDisposition' => '<string>',
             'ContentEncoding' => '<string>',
             'ContentLanguage' => '<string>',
             'ContentLength' => <integer>,
             'ContentRange' => '<string>',
             'ContentType' => '<string>',
             'DeleteMarker' => true || false,
             'ETag' => '<string>',
             'Expiration' => '<string>',
             'Expires' => <DateTime>,
             'LastModified' => <DateTime>,
             'Metadata' => ['<string>', ...],
             'MissingMeta' => <integer>,
             'ObjectLockLegalHoldStatus' => 'ON|OFF',
             'ObjectLockMode' => 'GOVERNANCE|COMPLIANCE',
             'ObjectLockRetainUntilDate' => <DateTime>,
             'PartsCount' => <integer>,
             'ReplicationStatus' => 'COMPLETE|PENDING|FAILED|REPLICA',
             'RequestCharged' => 'requester',
             'Restore' => '<string>',
             'SSECustomerAlgorithm' => '<string>',
             'SSECustomerKeyMD5' => '<string>',
             'SSEKMSKeyId' => '<string>',
             'ServerSideEncryption' => 'AES256|aws:kms',
             'StorageClass' => 'STANDARD|REDUCED_REDUNDANCY|STANDARD_IA|ONEZONE_IA|INTELLIGENT_TIERING|GLACIER|DEEP_ARCHIVE|OUTPOSTS|GLACIER_IR',
             'TagCount' => <integer>,
             'VersionId' => '<string>',
             'WebsiteRedirectLocation' => '<string>',
             */
            return $ret["Body"];
            
        } catch (S3Exception $e) {
            throw new \Exception("minio get object error");
        }
    }

    /**
     * 参考：https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#createmultipartupload
     */
    public static function createMultipartUpload($key)
    {
        try {
            $cfg = new MinioConfig();
            $c = MinioTool::client();
            $ret = $c->createMultipartUpload([
                "Bucket"=>$cfg->bucket,
                "Key"=>$key,
                "ContentType"=>MimeMapping::getType($key)]);
            return $ret["UploadId"];
        } 
        catch (S3Exception $e) {
            throw new \Exception("minio create error");
        }
    }

    /*
      参考：https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#uploadpart
      语法：  
        $result = $client->uploadPart([
            'Body' => <string || resource || Psr\Http\Message\StreamInterface>,
            'Bucket' => '<string>', // REQUIRED
            'ChecksumAlgorithm' => 'CRC32|CRC32C|SHA1|SHA256',
            'ChecksumCRC32' => '<string>',
            'ChecksumCRC32C' => '<string>',
            'ChecksumSHA1' => '<string>',
            'ChecksumSHA256' => '<string>',
            'ContentLength' => <integer>,
            'ContentSHA256' => '<string>',
            'ExpectedBucketOwner' => '<string>',
            'Key' => '<string>', // REQUIRED
            'PartNumber' => <integer>, // REQUIRED
            'RequestPayer' => 'requester',
            'SSECustomerAlgorithm' => '<string>',
            'SSECustomerKey' => '<string>',
            'SSECustomerKeyMD5' => '<string>',
            'SourceFile' => '<string>',
            'UploadId' => '<string>', // REQUIRED
        ]);
     */
    public static function uploadPart($key,$part,$id,$data)
    {
        try {
            $cfg = new MinioConfig();
            $c = MinioTool::client();
            $ret = $c->uploadPart([
                "Body"=>$data,
                "Bucket"=>$cfg->bucket,
                "ContentLength"=>strlen($data),
                "Key"=>$key,
                "PartNumber"=>$part,
                "UploadId"=>$id
            ]);
            $etag = str_replace("\"","",$ret["ETag"]);
            return $etag;
        } 
        catch (S3Exception $e) {
            throw new \Exception("minio upload part error");
        }
    }

    /*
     参考：https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#completemultipartupload
     语法：
        $result = $client->completeMultipartUpload([
        'Bucket' => '<string>', // REQUIRED
        'ChecksumCRC32' => '<string>',
        'ChecksumCRC32C' => '<string>',
        'ChecksumSHA1' => '<string>',
        'ChecksumSHA256' => '<string>',
        'ExpectedBucketOwner' => '<string>',
        'Key' => '<string>', // REQUIRED
        'MultipartUpload' => [
            'Parts' => [
                [
                    'ChecksumCRC32' => '<string>',
                    'ChecksumCRC32C' => '<string>',
                    'ChecksumSHA1' => '<string>',
                    'ChecksumSHA256' => '<string>',
                    'ETag' => '<string>',
                    'PartNumber' => <integer>,
                ],
                // ...
            ],
        ],
        'RequestPayer' => 'requester',
        'SSECustomerAlgorithm' => '<string>',
        'SSECustomerKey' => '<string>',
        'SSECustomerKeyMD5' => '<string>',
        'UploadId' => '<string>', // REQUIRED
        ]);
     */
    public static function completeMultipartUpload(/*FileInf*/$file){
        try {
            $parts = array();
            foreach($file->etags() as $b)
            {
                $parts[]=array(
                    "ETag"=>$b["etag"],
                    "PartNumber"=>$b["blockIndex"]
                );
            }
            
            $cfg = new MinioConfig();
            $c = MinioTool::client();
            $ret = $c->completeMultipartUpload([
                "Bucket"=>$cfg->bucket,
                "Key"=>$file->s3key(),
                "UploadId"=>$file->object_id,
                "MultipartUpload"=>[
                    "Parts"=>$parts
                ]
            ]);
            
        } 
        catch (S3Exception $e) {
            throw new \Exception("minio complete multipart error");
        }
    }
    
    /**
     * 向文件中写入块数据
     * @param long $blockOffset
     * @param long $lenLoc
     * @param string $pathSvr
     * @param string $tmpFile
     */
    function write($blockOffset,$lenLoc,$pathSvr,$data)
    {
        //写入数据
        $hfile = fopen($pathSvr,"r+b");
        //定位到续传位置
        fseek($hfile, intval($blockOffset),SEEK_SET);
        fwrite($hfile,$data);
        fclose($hfile);
    }
}
?>